﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;

namespace Project
{

    public class Player : GameEntity
    {
        private float yaw = 0;
        private float pitch = 0;
        private float sensitivity = 0.1f;
        // private float roll = 0;
        private const float maxPitch = (float)(0.95 * Math.PI / 2.0f);
        private float angularSpeed = (float)Math.PI / 100.0f;
        private MouseState originalMouseState;

        public int lives { get; protected set; }
        private Vector3 checkpoint;
        private Quaternion checkpointOrientation;

        public void setCheckpoint()
        {
            checkpoint = position;
            checkpointOrientation = orientation;
        }

        private Hook hook;
        private void shootHook(GameTime gt, GameState gs)
        {
            if (hook != null)
                releaseHook(gt, gs);
            hook = new Hook(position, orientation, this);
            gs.spawnEntity(hook);
        }
        public void releaseHook(GameTime gt, GameState gs)
        {
            if (hook != null)
                hook.kill();
            hook = null;
        }

        public Player(Vector3 position, Quaternion orientation)
            : base(position, orientation, 100)
        {
            // don't smooth out our mouse movement very much
            slerpConstant = 0.90f;
            Vector3 forward = Vector3.Transform(Vector3.Forward, orientation);
            yaw = (float)Math.Atan2(forward.Z, forward.X);
            pitch = (float)(Math.Acos(Vector3.Dot(forward, Vector3.Down)) - Math.PI / 2);

            setCheckpoint();
            lives = 10;

            collisionObject = new CollisionObject(
                new AABB(new Vector3(-0.7f, -1.5f, -0.7f), new Vector3(0.7f, 0.2f, 0.7f), position),
                delegate(CollisionData data) { }, this
            );
        }

        public override void init(GameState gs)
        {
            base.init(gs);

            Mouse.SetPosition(gs.getGraphics().Viewport.Width / 2, gs.getGraphics().Viewport.Height / 2);
        }

        public override bool update(GameTime gt, GameState gs)
        {
            if (health <= 0)
            {
                gs.getSoundManager().playSound("sounds\\boom1", 1.0f, false);
                lives--;
                position = checkpoint;
                velocity = Vector3.Zero;
                orientation = checkpointOrientation;
                targetOrientation = orientation;
                health = 100;
                releaseHook(gt, gs);
            }
            if (lives <= 0)
            {
                gs.onLose();
                return false;
            }
            updateInput(gt, gs);

            //Play footsteps
            SoundManager sm = gs.getSoundManager();
            if (velocity != Vector3.Zero)
            {
                if (isOnGround(gs))
                    sm.playSound("sounds\\footsteps-4", 1, false);
            }
            else
            {
                sm.pauseSound("sounds\\footsteps-4");
            }

            if (isOnGround(gs))
                this.frictionConstant = 0.20f;
            else
                this.frictionConstant = 0.05f; // decreased friction against walls

            return base.update(gt, gs);
        }

        private bool isOnGround(GameState gs)
        {
            AABB footBounds = collisionObject.collisionBox;
            footBounds.position += Vector3.Up * 0.1f;
            footBounds.min.X *= 0.9f;
            footBounds.min.Z *= 0.9f;
            footBounds.max.X *= 0.9f;
            footBounds.max.Z *= 0.9f;
            return gs.getCollisionManager().collides(footBounds);
        }

        public void updateInput(GameTime gt, GameState gs)
        {
            Vector3 acceleration = Vector3.Zero;
            acceleration = Vector3.Zero;
            bool onGround = isOnGround(gs);

            GamePadState currentState = GamePad.GetState(PlayerIndex.One);
            if (currentState.IsConnected)
            {
                acceleration += new Vector3(0, 0, currentState.ThumbSticks.Left.Y * 50);
                acceleration += new Vector3(0, currentState.ThumbSticks.Left.X * 50, 0);

                yaw -= angularSpeed * sensitivity * currentState.ThumbSticks.Right.X * 20;
                pitch += angularSpeed * sensitivity * currentState.ThumbSticks.Right.Y * 20;

                if (currentState.Buttons.RightShoulder == ButtonState.Pressed)
                    this.shootHook(gt, gs);
                else if (currentState.Buttons.RightShoulder == ButtonState.Released)
                    this.releaseHook(gt, gs);

                if (currentState.Buttons.LeftShoulder == ButtonState.Released && isOnGround(gs))
                {
                    acceleration *= 2.0f; // if we are running, go twice as fast just before jumping
                    acceleration += new Vector3(0, -350.0f, 0);
                }
            }

            MouseState currentMouseState = Mouse.GetState();
            if (currentMouseState != originalMouseState)
            {
                float xDifference = currentMouseState.X - originalMouseState.X;
                float yDifference = currentMouseState.Y - originalMouseState.Y;
                yaw -= angularSpeed * sensitivity * xDifference;
                pitch -= angularSpeed * sensitivity * yDifference;
                Mouse.SetPosition(gs.getGraphics().Viewport.Width / 2, gs.getGraphics().Viewport.Height / 2);
            }

            if (currentMouseState.LeftButton == ButtonState.Pressed && originalMouseState.LeftButton != ButtonState.Pressed)
                this.shootHook(gt, gs);
            else if (currentMouseState.LeftButton == ButtonState.Released && originalMouseState.LeftButton != ButtonState.Released)
                this.releaseHook(gt, gs);
            originalMouseState = Mouse.GetState();

            if (Keyboard.GetState().IsKeyDown(Keys.W))
            {
                acceleration += new Vector3(0, 0, 1.0f);
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.S))
            {
                acceleration += new Vector3(0, 0, -1.0f);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.A))
            {
                acceleration += new Vector3(1.0f, 0, 0);
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.D))
            {
                acceleration += new Vector3(-1.0f, 0, 0);
            }
            acceleration = Vector3.Transform(acceleration, Matrix.CreateFromQuaternion(orientation));
            acceleration.Y = 0;
            if (acceleration.LengthSquared() > 0)
                acceleration.Normalize();
            if (!onGround)
                acceleration *= 10.0f; // slower maximum lateral acceleration while in the air
            if (isOnGround(gs))
            {
                acceleration *= 60.0f; // greater maximum lateral acceleration while on the ground
                if (Keyboard.GetState().IsKeyDown(Keys.Space))
                {
                    // acceleration *= 2.0f; // if we are running, go twice as fast just before jumping
                    acceleration += new Vector3(0, -450.0f, 0);
                }
            }

            applyForce(acceleration);

            if (Keyboard.GetState().IsKeyDown(Keys.Right))
            {
                yaw -= angularSpeed;
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.Left))
            {
                yaw += angularSpeed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
            {
                pitch += angularSpeed;
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                pitch -= angularSpeed;
            }
            if (pitch < -maxPitch)
                pitch = -maxPitch;
            if (pitch > maxPitch)
                pitch = maxPitch;
            targetOrientation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0);

        }
    }
}
